using System;
using System.Collections.Generic;
using System.Text;

namespace Avanzis.Helpers.PersistentObjects
{
    /// <summary>
    /// Abstract <seealso cref="ISessionScope"/> implementation
    /// </summary>
    public abstract class AbstractScope : MarshalByRefObject, ISessionScope
    {
        private readonly SessionScopeType type;

        private readonly FlushAction flushAction;

        protected Dictionary<object, Session> key2Session = new Dictionary<object, Session>();

        public AbstractScope(FlushAction flushAction, SessionScopeType type)
        {
            this.flushAction = flushAction;
            this.type = type;

            ThreadScopeAccessor.Instance.RegisterScope(this);
        }

        /// <summary>
        /// Returns the <see cref="SessionScopeType"/> defined 
        /// for this scope
        /// </summary>
        public SessionScopeType ScopeType
        {
            get { return type; }
        }

        /// <summary>
        /// Returns the <see cref="ISessionScope.FlushAction"/> defined 
        /// for this scope
        /// </summary>
        public FlushAction FlushAction
        {
            get { return flushAction; }
        }

        /// <summary>
        /// Flushes the sessions that this scope 
        /// is maintaining
        /// </summary>
        public void Flush()
        {
            foreach (Session session in GetSessions())
            {
                // session.Flush();
            }
        }

        public virtual bool IsKeyKnown(object key)
        {            
            return key2Session.ContainsKey(key);
        }

        public virtual void RegisterSession(object key, Session session)
        {
            key2Session.Add(key, session);

            Initialize(session);
        }

        public virtual Session GetSession(object key)
        {
            return key2Session[key] as Session;
        }

        public virtual bool WantsToCreateTheSession
        {
            get { return true; }
        }

        public virtual Session OpenSession(SessionFactory sessionFactory)
        {
            Session session = sessionFactory.OpenSession();

            SetFlushMode(session);

            return session;
        }

        public void Dispose()
        {
            ThreadScopeAccessor.Instance.UnRegisterScope(this);

            Session[] sessions = new Session[key2Session.Count];

            key2Session.Values.CopyTo(sessions, 0);

            PerformDisposal(sessions);
        }

        protected virtual void Initialize(Session session)
        {
        }

        protected virtual void PerformDisposal(IList<Session> sessions)
        {
        }

        protected internal void PerformDisposal(IList<Session> sessions, bool flush, bool close)
        {
            foreach (Session session in sessions)
            {
                if (flush) session.Flush();
                if (close) session.Close();
            }
        }

        protected internal virtual void DiscardSessions(IList<Session> sessions)
        {
            foreach (Session session in sessions)
            {
                RemoveSession(session);
            }
        }

        protected void SetFlushMode(Session session)
        {
            if (FlushAction == FlushAction.Auto)
            {
                session.FlushMode = FlushMode.Auto;
            }
            else if (FlushAction == FlushAction.Never)
            {
                session.FlushMode = FlushMode.Never;
            }
        }

        internal IList<Session> GetSessions()
        {
            Session[] sessions = new Session[key2Session.Count];

            key2Session.Values.CopyTo(sessions, 0);
            
            return key2Session.Values;
        }

        private void RemoveSession(Session session)
        {            
            foreach (KeyValuePair<object, Session> entry in key2Session)
            {
                if (Object.ReferenceEquals(entry.Value, session))
                {
                    session.Close();
                    key2Session.Remove(entry.Key);
                    break;
                }
            }
        }
    }
}
